package com.zdsoft.site.event;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
 * 出现异常对程序的影响：
	1.可以某些事件为同步、另外一部分为异步.		  
	2.异步事件抛出异常,不影响后面侦听器的执行,可通过实现AsyncUncaughtExceptionHandler接口捕获异步抛出的异常.
	3.同步事件抛出异常,会导致后面的侦听器不执行。可直接在发布事件的地方捕获到该异常。
	4.不管同步和异步,(触发事件的位置不在事务中,事件本身也不在事务中）
	以上所有设置暴露一个真正的问题。现在，我们虽然使用其他线程发送一个异步消息处理。不幸的是，我们引入竞争条件。
    1.开始事务
    2.存储订单数据到数据库
    3.发送一个包装order的异步消息
    4.确认

	异步线程获得OrderPlacedEvent并开始处理。
	现在的问题是，它发生（3）之后，还是（4）之前或者（4）之后？这有一个很大的区别！
	在前者的情况下，交易也尚未提交订单所以不存在于数据库中。另一方面，延迟加载可能已经在工作，
	致使订单对象仍然然绑定在 PersistenceContext（缺省我们使用JPA）。  
	解决办法是使用 TransactionSynchronizationManager 
 * 同步事件发生异常，会出现后面的事件(包括同步事件和异步事件)无法执行
 * 用来在当前事务完成之后再发送事件
 * @author cqyhm
 */
@Component
public class TransactionEventPublisher implements ApplicationEventPublisher{
	@Autowired
	private ApplicationEventPublisher publisher;
	private Logger logger=LoggerFactory.getLogger(getClass());
	public TransactionEventPublisher(){
	}
	public TransactionEventPublisher(ApplicationEventPublisher publisher){
		this.publisher=publisher;
	}
	@Override
	public void publishEvent(final ApplicationEvent event) {
		Assert.notNull(publisher);
		//如果在实际活动的事务当中,才需要处理
		if(TransactionSynchronizationManager.isActualTransactionActive()){
			 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
				@Override
				public void afterCommit() {
					logger.info("上个事务提交之后触发事件");
					publisher.publishEvent(event);
				}
			 });
		}else{
			logger.info("上个事务还未提交或没有事务触发事件");
			publisher.publishEvent(event);
		}
	}
	@Override
	public void publishEvent(final Object event) {
		//如果在实际活动的事务当中,才需要处理
		if(TransactionSynchronizationManager.isActualTransactionActive()){
			 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
				@Override
				public void afterCommit() {
					publisher.publishEvent(event);
				}
			 });
		}else{
			publisher.publishEvent(event);
		}
	}
}
